//////////////////////////////////////////////////////////////////////
//
//	ZETeam source code 
//	Copyright (c) ZETeam 2011-..
//	
//	File: CAngles.h
//  Description: Angles classes. Based on Id Software source code.
// 
//	History:
//	- February 27, 2011: Created by Max Streltsov
//
//////////////////////////////////////////////////////////////////////

#ifndef CANGLES_H
#define CANGLES_H

#include "CMath.h"
#include "CVector.h"
#include "CMatrix.h"
#include "CRotation.h"
#include "CQuat.h"
/*
===============================================================================

	Euler angles

===============================================================================
*/

// angle indexes
#define	PITCH				0		// up / down
#define	YAW					1		// left / right
#define	ROLL				2		// fall over

class CVec3;
class CQuat;
class CRotation;
class CMat3;
class CMat4;

class CAngles {
public:
	float			pitch;
	float			yaw;
	float			roll;

					CAngles( void );
					CAngles( float pitch, float yaw, float roll );
					explicit CAngles( const  CVec3 &v );

	void 			set( float pitch, float yaw, float roll );
	CAngles &		zero( void );

	float			operator[]( int index ) const;
	float &			operator[]( int index );
	CAngles		operator-() const;			// negate angles, in general not the inverse rotation
	CAngles &		operator=( const CAngles &a );
	CAngles		operator+( const CAngles &a ) const;
	CAngles &		operator+=( const CAngles &a );
	CAngles		operator-( const CAngles &a ) const;
	CAngles &		operator-=( const CAngles &a );
	CAngles		operator*( const float a ) const;
	CAngles &		operator*=( const float a );
	CAngles		operator/( const float a ) const;
	CAngles &		operator/=( const float a );

	friend CAngles	operator*( const float a, const CAngles &b );

	bool			compare( const CAngles &a ) const;							// exact compare, no epsilon
	bool			compare( const CAngles &a, const float epsilon ) const;	// compare with epsilon
	bool			operator==(	const CAngles &a ) const;						// exact compare, no epsilon
	bool			operator!=(	const CAngles &a ) const;						// exact compare, no epsilon

	CAngles &		normalize360( void );	// normalizes 'this'
	CAngles &		normalize180( void );	// normalizes 'this'

	void			clamp( const CAngles &min, const CAngles &max );

	int				getDimension( void ) const;

	void			toVectors( CVec3 *forward, CVec3 *right = NULL, CVec3 *up = NULL ) const;
	CVec3			toForward( void ) const;
	CQuat			toQuat( void ) const;
	CRotation		toRotation( void ) const;
	CMat3			toMat3( void ) const;
	CMat4			toMat4( void ) const;
	CVec3			toAngularVelocity( void ) const;
	const float *	toFloatPtr( void ) const;
	float *			toFloatPtr( void );
	//const char *	toString( int precision = 2 ) const;
};

extern CAngles ang_zero;

Z_INLINE CAngles::CAngles( void ) {
}

Z_INLINE CAngles::CAngles( float pitch, float yaw, float roll ) {
	this->pitch = pitch;
	this->yaw	= yaw;
	this->roll	= roll;
}

// wtf?
Z_INLINE CAngles::CAngles(const CVec3 &v ) {
	this->pitch = v[0];
	this->yaw	= v[1];
	this->roll	= v[2];
}

Z_INLINE void CAngles::set( float pitch, float yaw, float roll ) {
	this->pitch = pitch;
	this->yaw	= yaw;
	this->roll	= roll;
}

Z_INLINE CAngles &CAngles::zero( void ) {
	pitch = yaw = roll = 0.0f;
	return *this;
}

Z_INLINE float CAngles::operator[]( int index ) const {
	assert( ( index >= 0 ) && ( index < 3 ) );
	return ( &pitch )[ index ];
}

Z_INLINE float &CAngles::operator[]( int index ) {
	assert( ( index >= 0 ) && ( index < 3 ) );
	return ( &pitch )[ index ];
}

Z_INLINE CAngles CAngles::operator-() const {
	return CAngles( -pitch, -yaw, -roll );
}

Z_INLINE CAngles &CAngles::operator=( const CAngles &a ) {
	pitch	= a.pitch;
	yaw		= a.yaw;
	roll	= a.roll;
	return *this;
}

Z_INLINE CAngles CAngles::operator+( const CAngles &a ) const {
	return CAngles( pitch + a.pitch, yaw + a.yaw, roll + a.roll );
}

Z_INLINE CAngles& CAngles::operator+=( const CAngles &a ) {
	pitch	+= a.pitch;
	yaw		+= a.yaw;
	roll	+= a.roll;

	return *this;
}

Z_INLINE CAngles CAngles::operator-( const CAngles &a ) const {
	return CAngles( pitch - a.pitch, yaw - a.yaw, roll - a.roll );
}

Z_INLINE CAngles& CAngles::operator-=( const CAngles &a ) {
	pitch	-= a.pitch;
	yaw		-= a.yaw;
	roll	-= a.roll;

	return *this;
}

Z_INLINE CAngles CAngles::operator*( const float a ) const {
	return CAngles( pitch * a, yaw * a, roll * a );
}

Z_INLINE CAngles& CAngles::operator*=( float a ) {
	pitch	*= a;
	yaw		*= a;
	roll	*= a;
	return *this;
}

Z_INLINE CAngles CAngles::operator/( const float a ) const {
	float inva = 1.0f / a;
	return CAngles( pitch * inva, yaw * inva, roll * inva );
}

Z_INLINE CAngles& CAngles::operator/=( float a ) {
	float inva = 1.0f / a;
	pitch	*= inva;
	yaw		*= inva;
	roll	*= inva;
	return *this;
}

Z_INLINE CAngles operator*( const float a, const CAngles &b ) {
	return CAngles( a * b.pitch, a * b.yaw, a * b.roll );
}

Z_INLINE bool CAngles::compare( const CAngles &a ) const {
	return ( ( a.pitch == pitch ) && ( a.yaw == yaw ) && ( a.roll == roll ) );
}

Z_INLINE bool CAngles::compare( const CAngles &a, const float epsilon ) const {
	if ( CZMath::Fabs( pitch - a.pitch ) > epsilon ) {
		return false;
	}
			
	if ( CZMath::Fabs( yaw - a.yaw ) > epsilon ) {
		return false;
	}

	if ( CZMath::Fabs( roll - a.roll ) > epsilon ) {
		return false;
	}

	return true;
}

Z_INLINE bool CAngles::operator==( const CAngles &a ) const {
	return compare( a );
}

Z_INLINE bool CAngles::operator!=( const CAngles &a ) const {
	return !compare( a );
}

Z_INLINE void CAngles::clamp( const CAngles &min, const CAngles &max ) {
	if ( pitch < min.pitch ) {
		pitch = min.pitch;
	} else if ( pitch > max.pitch ) {
		pitch = max.pitch;
	}
	if ( yaw < min.yaw ) {
		yaw = min.yaw;
	} else if ( yaw > max.yaw ) {
		yaw = max.yaw;
	}
	if ( roll < min.roll ) {
		roll = min.roll;
	} else if ( roll > max.roll ) {
		roll = max.roll;
	}
}

Z_INLINE int CAngles::getDimension( void ) const {
	return 3;
}

Z_INLINE const float *CAngles::toFloatPtr( void ) const {
	return &pitch;
}

Z_INLINE float *CAngles::toFloatPtr( void ) {
	return &pitch;
}

#endif /* CANGLES_H */
